<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body style="background-color: antiquewhite;">

  <div>
    <h4>diff算法</h4>
    <p style="white-space: pre;">
      在patch方法时生成真实的dom节点挂载到this.$el上，此时可能会进入diff算法比较:
      1. 如果是第一次渲染不会diff比较dom节点，只会直接渲染，替换掉空的div id=app的节点
      2. 如果是第一次渲染后，发生了数据改变，而且引起了dom的改变，此时，会对比前后的虚拟节点进行diff算法
      3. diff对比的是虚拟节点，对比完后直接操作了dom，而对应的dom存储在对应虚拟节点上，这是因为第一次进行渲染的时候，
      走到patch方法时，直接渲染了虚拟节点，在递归生成真实dom的过程中，直接将生成的dom挂到了vdom的el属性上，
      <strong>如此，每一次挂载后的虚拟节点，也就是下一轮更新时的旧的vdom其每一个节点都会又el指向真实dom，以便于操作</strong>
    </p>
    <p style="white-space: pre;">
      diff算法具体实现：（首先diff算法只进行新旧dom的平级比较，从根节点开始）
      在patch方法内，传入新旧vdom，此后比较，首先对本（根）节点进行比较，如果是不是同一个节点（tag和key值不同），则直接替换
      如果相同，直接复用节点，在判断是否为文本节点，如果时文本节点直接把不同的text替换即可，如果时标签则会比较属性，旧的多旧删除，新的多则赋值（这里直接将所有新的vdom属性赋值一遍，旧的不同就覆盖，一样则不变
      注意：如果新旧属性相同使用setAttribute重复设置相同的属性时，dom不会变化！！ 可以理解为浏览器的自动优化）

      接下来比较孩子，如果新有旧没有，加孩子，旧有新没有删除，<strong>如果此时都有孩子又会进行diff比较孩子节点</strong>
      孩子节点比较（递归），采用双指针的方式：即头尾都有指针，进行循环比较，只要出现一方是头大于尾指针则退出循环；
      以下出现的节点相同均指key与tagName相同；<strong>注意：diff算法操作dom指的是旧的dom，也就是旧的vnode的el属性</strong>
      <p style="white-space: pre;">
        1. 先进行头头指针比较，如果头头时同一个vdom，则会直接递归比较这两个节点，并头头指针++，否则进入下一个分支
        2. 再比较尾指针，如果vdom相同，也会直接递归比较这两个节点，并尾指针--，否则进入下一个分支
        3. 此后进行旧头新尾比较，如果旧头与新尾vdom相同，也会递归比较两个节点，并且直接使用insertBefore把旧头移动到旧尾
        （因为新的dom尾巴就是此节点，注意：insertBefore又移动节点的效果），并且旧头++新尾--，否则进入下一分支
        4. 再进行旧尾新头比较，如果旧尾与新头相同，也会递归比较这两个vdom，并且使用insertBefore把旧尾移动到旧头
        **  以上4个分支进行的处理主要指针对优化，比如追加了尾节点，头节点，或逆序，或以上三种情况组合，走到此分支说明这个节点是“乱序”  **
        5. 最后进入乱序处理的分支，此时拿到旧vdom的映射表map，key为节点的key，value为vdom序号
        此时为保证最大程度复用旧节点，拿到新节点的头指针指向的元素的key，直接进行map[key]取值，
        取到了直接复用旧节点，直接把此旧节点移动到旧头指针元素前面；新头指针++
        取不到直接把新节点头指针vdom创建的真实dom insertBefore 移到旧节点的头指针元素前面，新头指针++
        6. 如此再一轮循环结束，在进行下一轮循环，知道触发循环结束条件
        7. 结束循环后，如果此时又哪一方的头指针还在尾指针的前面（或相等），说明另一方移动到底了，但是这一方没有到底
        此时直接这一方有多的节点，旧需要处理，如果是新的vdom的一方多，那就要全部追加到老vdom后面；如果是老的多，直接全删了即可

        如此逻辑---即diff算法，核心再比较孩子节点，采用双指针比较
      </p>
    </p>
    <strong>  
      注意，在while循环的前4个分支里（头头比，尾尾比，以及头尾尾头比），都是为了优化，
      比如数据驱动节点更新，数组的push，pop，unshift，shift，reverse或混合等等
      最后一个是乱序，但是也使用旧vnode的索引表，生成map最大程度复用旧节点
    
      在patchVnode的递归和循环中，不停频繁的操作dom，但是这是直接操作，浏览器会自动优化，一起移动dom
      而不会多次重绘重排</strong>
  </div>

  <div id="app" a="1" fqm="fqmyysjjd" style="color: red">
    <div>fqmyysjjd</div>
  </div>

  <script src="vue.js"></script>
  <!-- 结合test/diff.js使用，再src/index内引入 -->
</body>

</html>